/*
 * ServiceRegistry.java
 *
 * Created on April 22, 2007, 12:37 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.xeerkat.agent.services;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;
import org.infoset.xml.Document;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.Element;
import org.infoset.xml.Location;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.sax.SAXDocumentLoader;

/**
 *
 * @author alex
 */
public class ServiceRegistry extends TreeMap<Name,Service>
{
   
   public static Logger LOG = Logger.getLogger(ServiceRegistry.class.getName());
   
   public static final String NAMESPACE_STRING = "http://www.xeerkat.org/Vocabulary/Services/2007/1/0";
   public static final URI NAMESPACE = URI.create(NAMESPACE_STRING);
   
   public static Name REGISTRY = Name.create(NAMESPACE,"registry");
   public static Name SERVICE = Name.create(NAMESPACE,"service");
   public static Name PARAMETER = Name.create(NAMESPACE,"parameter");
   public static Name LIBRARY = Name.create(NAMESPACE,"library");
   
   DocumentLoader loader;
   String href;
   URI location;
   Name registryName;
   /** Creates a new instance of ServiceRegistry */
   public ServiceRegistry()
   {
      this(null,null);
   }
   public ServiceRegistry(String href,URI location)
   {
      this.location = location;
      this.href = href;
      this.loader = new SAXDocumentLoader();
   }
   
   public void setLocationReference(String href)
   {
      this.href = href;
   }
   
   public void setLocation(URI location) {
      this.location = location;
   }
   
   public String getLocationReference() {
      return href;
   }
   
   public URI getLocation() {
      return location;
   }
   
   public Name getName() {
      return registryName;
   }
   
   public void load() 
      throws IOException,XMLException
   {
      Document registryDoc = loader.load(location);
      load(registryDoc.getDocumentElement());
   }
   
   public void load(Element top)
      throws IOException,XMLException
   {
      if (!top.getName().equals(REGISTRY)) {
         throw new XMLException("Unrecognized document element "+top.getName()+", expecting "+REGISTRY);
      }
      String sname = top.getAttributeValue("name");
      registryName = sname==null ? null : top.getNamespaceScope().expandName(sname,false);
      
      Iterator<Element> services = top.getElementsByName(SERVICE);
      while (services.hasNext()) {
         Element serviceE = services.next();
         Service service = createService(serviceE);
         if (get(service.getName())==null) {
            put(service.getName(),service);
         } else {
            LOG.warning("A service already exists for name "+service.getName()+". Second definition has been ignored.");
         }
      }
   }
   
   public static Service createService(Element serviceE)
      throws IOException,XMLException
   {
      String href = serviceE.getAttributeValue("href");
      String className = serviceE.getAttributeValue("class");
      if (href==null && className==null) {
         throw new XMLException("Either the 'href' or 'class' attribute must be specified.",(Location)serviceE);
      }
      String path = serviceE.getAttributeValue("path");
      if (path==null) {
         throw new XMLException("Missing the 'path' attribute.",(Location)serviceE);
      }
      String qname = serviceE.getAttributeValue("name");
      if (qname==null) {
         throw new XMLException("Missing the 'name' attribute.",(Location)serviceE);
      }
      String interfaceS = serviceE.getAttributeValue("interface");
      Name name = serviceE.getNamespaceScope().expandName(qname,false);
      URI implLoc = href==null ? null : serviceE.getBaseURI().resolve(href);
      Map<String,String> parameters = new TreeMap<String,String>();
      Iterator<Element> parameterElements = serviceE.getElementsByName(PARAMETER);
      while (parameterElements.hasNext()) {
         Element parameterE = parameterElements.next();
         String pname = parameterE.getAttributeValue("name");
         if (pname==null) {
            continue;
         }
         String value = parameterE.getAttributeValue("value");
         if (value==null) {
            href = parameterE.getAttributeValue("href");
            if (href!=null) {
               value = parameterE.getBaseURI().resolve(href).toString();
            }
         }
         if (value==null) {
            value = "";
         }
         parameters.put(pname,value);
      }
      List<URL> libraries = null;
      Iterator<Element> libraryElements = serviceE.getElementsByName(LIBRARY);
      while (libraryElements.hasNext()) {
         Element libraryE = libraryElements.next();
         href = libraryE.getAttributeValue("href");
         if (href==null) {
            continue;
         }
         if (libraries==null) {
            libraries =  new ArrayList<URL>();
         }
         libraries.add(libraryE.getBaseURI().resolve(href).toURL());
      }
      try {
         URI interfaceU = interfaceS==null ? null : new URI(interfaceS);
         Service service = new Service(interfaceU,name,path,implLoc,className);
         service.setParameters(parameters);
         service.setLibraries(libraries);
         return service;
      } catch (URISyntaxException ex) {
         throw new XMLException("Cannot create URI from interface value "+interfaceS+": "+ex.getMessage(),ex);
      }
      
   }
   
}
